<?php
declare(strict_types=1);

namespace Zms\Guard;

use Hyperf\Context\ApplicationContext;
use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Di\Aop\{AbstractAspect, ProceedingJoinPoint};
use Hyperf\Di\Exception\Exception;
use Hyperf\HttpServer\Contract\RequestInterface;
use Zms\Exception\{AppException, DataException, ExpireLoggedInException, NotLoggedException};
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Zms\AdminHelper\Client;
use Zms\AdminHelper\Ip;
use Zms\Guard\Event\LoggerEvent;
use Zms\Unit\Arr;
use function Hyperf\Support\env;
use Psr\EventDispatcher\EventDispatcherInterface;

#[Aspect(priority: 100)]
class AuthAspect extends AbstractAspect
{

    /**
     * 要切入的注解
     * @var array
     */
    public array $annotations = [
        Auth::class
    ];
    #[Inject]
    protected RequestInterface $request;

    /**
     * @param ProceedingJoinPoint $proceedingJoinPoint
     * @return mixed
     * @throws AppException
     * @throws ContainerExceptionInterface
     * @throws DataException
     * @throws Exception
     * @throws ExpireLoggedInException
     * @throws NotFoundExceptionInterface
     * @throws NotLoggedException
     */
    public function process(ProceedingJoinPoint $proceedingJoinPoint): mixed
    {
        $permissions = AuthUnit::getActionPermissions($proceedingJoinPoint->className, $proceedingJoinPoint->methodName);
        if ($permissions === null) {//没有配置权限节点
            return $proceedingJoinPoint->process();
        }
        $guard = UserAuth::guard($permissions['guard']);
        $permissions['user_id'] = $guard->tourist() === true ? -1 : $guard->uid();
        if ($permissions['process'] === false) {
            $this->logger($permissions);
            return $proceedingJoinPoint->process();
        }
        if ($permissions['tourist'] === true) {//允许游客访问
            $this->logger($permissions);
            return $proceedingJoinPoint->process();
        }
        if (!(in_array('*', $permissions['mode']) || in_array(env('APP_ENV'), $permissions['mode']))) {//判断接口是否环境可用
            throw new AppException('当前环境不允许访问');
        }
        if ($permissions['user_id'] === -1) {//未登录
            $this->request->hasHeader('auth-token') ? throw new ExpireLoggedInException() : throw new NotLoggedException();
        }
        if ($permissions['check'] === true && $permissions['sign'] && $permissions['sign'] !== 'skip') {
            if ($guard->user()->authorize($permissions)) {//权限验证
                $this->logger($permissions);
                return $proceedingJoinPoint->process();
            }
        } else {//无需权限,自定义验证
            $this->logger($permissions);
            return $proceedingJoinPoint->process();
        }
        throw new AppException('你没有访问权限');
    }


    /**
     * 记录日志
     * @param array $permissions
     * @return void
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function logger(array $permissions): void
    {
        if ($permissions['logger'] === false) {
            return;//未开启记录
        }
        $permissions['name'] = Arr::join($permissions['name'], '/');
        //记录请求类型
        $permissions['method'] = $this->request->getMethod();
        if (($permissions['logger'] === 'GET' || $permissions['logger'] === 'get') && $permissions['method'] !== 'GET') {
            return;//记录条件不符
        }
        if (($permissions['logger'] === 'POST' || $permissions['logger'] === 'post') && $permissions['method'] !== 'POST') {
            return;//记录条件不符
        }
        //请求参数
        $permissions['param'] = $permissions['record'] ? $this->request->inputs($permissions['record']) : $this->request->all();
        //请求IP
        $permissions['ip'] = Ip::getIp();
        //请求地址
        $permissions['url'] = $this->request->getUri()->getPath();
        //请求系统
        $permissions['os'] = Client::os();
        //所属浏览器
        $permissions['browser'] = Client::browser();
        //具体描述
        $permissions['describe'] = $permissions['describe'] ?? '';//如果手动设置了数据
        //日志记录
        ApplicationContext::getContainer()->get(EventDispatcherInterface::class)->dispatch(new LoggerEvent($permissions));
    }
}